package framework

/*
 * game server framework.
 */
import (
	"GameServer/config"
	"GameServer/csv"
	"GameServer/mysql"
	"GameServer/net"
	"GameServer/role"
	"GameServer/snowflake"
	"GameServer/timer"
	"GameServer/utils"
	"fmt"
	"math/rand"
	"mnet"
	log "myLog"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

type FrameWork struct {
	exitSignal chan os.Signal
	server     mnet.IServer
}

func (f *FrameWork) Init(path string) error {
	rand.Seed(int64(time.Now().Second()))

	f.exitSignal = make(chan os.Signal, 1)
	signal.Notify(f.exitSignal, syscall.SIGINT, syscall.SIGTERM)

	// initialize log.
	if !log.Init("game", log.LogDebug, "", path+"/log") {
		return fmt.Errorf("init log error")
	}

	// load GameServer.conf config.
	configPath := path + "/GameServer.conf"
	if err := config.ConfigObj.Load(configPath); err != nil {
		log.Errorf("load %s error:%+v", configPath, err)
		return err
	}

	// set log level.
	log.SetLevel(int(config.ConfigObj.LogLevel))

	// initialize mysql module.
	if err := f.initMysql(); err != nil {
		log.Errorf("init mysql error:%+v", err)
		return err
	}

	// initialize timer.
	timer.TimerMgrObj.Init()

	// initialize snow flake.
	if err := snowflake.SnowflakeIdWorkerObj.InitIdWorker(1, 1); err != nil {
		log.Errorf("initialize snow flake error:%+v", err)
		return err
	}

	// load all csv config
	if err := csv.ConfigMgrObj.LoadConfig(path + "/csv"); err != nil {
		log.Errorf("csv.ConfigMgrObj.Load config error:%+v", err)
		return err
	}

	return nil
}

func (f *FrameWork) initMysql() error {
	if err := f.createMysqlProxy(mysql.RoleModuleLogicId); err != nil {
		return err
	}
	return nil
}

// createMysqlProxy create mysql proxy with LogicId.
func (f *FrameWork) createMysqlProxy(LogicId uint32) error {
	// hosts ip:port;ip:port
	hosts := strings.Split(config.ConfigObj.Host, ";")

	// db host should be: ip:port
	hostInfo := strings.Split(hosts[0], ":")
	if len(hostInfo) != 2 {
		return fmt.Errorf("db host should be ip:port format,it is %s form", config.ConfigObj.Host)
	}
	port, err := utils.AtoInt32(hostInfo[1])
	if err != nil {
		return err
	}

	mysqlConfig := &mysql.DBConf{
		Host:        hostInfo[0],
		Port:        uint16(port),
		Database:    config.ConfigObj.DBName,
		User:        config.ConfigObj.User,
		Password:    config.ConfigObj.Passwd,
		Charset:     config.ConfigObj.Charset,
		MaxOpenConn: config.ConfigObj.MaxConn,
		MaxIdleConn: config.ConfigObj.IdleConn,
		MaxLifeTime: time.Duration(config.ConfigObj.MaxLife) * time.Minute,
	}
	if _, err := mysql.MysqlProxyObj.CreateMysqlProxy(LogicId, mysqlConfig); err != nil {
		return err
	}
	return nil
}

// StartListen start tcp listen.
func (f *FrameWork) StartListen() error {
	if err := f.startServer(config.ConfigObj.ListenAddr); err != nil {
		log.Errorf("Listen error:%+v", err.Error())
		return err
	} else {
		log.Infof("start listening on: %s ok", config.ConfigObj.ListenAddr)
		return nil
	}
}

// startServer start to listen on addr.
func (f *FrameWork) startServer(addr string) error {
	sessionFactory := &net.ClientFactoryObj
	sessionFactory.Init()
	codec := mnet.CreateBigCodec()
	f.server = mnet.GetNetInstance().CreateServer(addr, sessionFactory, codec, nil, false, 0)
	if !f.server.StartListen() {
		return fmt.Errorf("listen on %s error", addr)
	}
	return nil
}

// Release release.
func (f *FrameWork) Release() {
	// mnet stop.
	if f.server != nil {
		f.server.Stop()
	}
	mnet.GetNetInstance().Stop()
	log.Infof("mnet is stopped")

	// timer object quit.
	timer.TimerMgrObj.Quit()
	log.Infof("TimerMgrObj is exit")

	// role manager's release.
	role.RoleMgrObj.Release()
	log.Infof("RoleMgrObj is released")

	// context wait.
	utils.ContextInfoObj.ExitAndWait()

	// GameServer is exit log out.
	log.Infof("GameServer is exited now")
}

// start to Run.
func (f *FrameWork) Run() {
	<-f.exitSignal
}
